#include "scene_graph.h"
#include "scene_graph_loader.h"
#include "scene_graph_manager.h"
#include "parser_list.h"
#include "matrix.h"

#include <fstream>

using namespace krryn::scene;
using namespace krryn::math;

scene_graph_node_base::scene_graph_node_base(const std::string &a_NodeName)
	: m_Parent(0), 
	  m_WorldMatrix(*(new math::matrix())), 
	  m_LocalMatrix(*(new math::matrix())),
	  m_Enabled(true),
	  m_Visible(true),
	  m_NodeName(a_NodeName)
{
	  scene_graph_manager::get_instance()->attach(this);
}

scene_graph_node_base::scene_graph_node_base(const std::string &a_NodeName, const std::string &a_Id)
	: m_Parent(0), 
	  m_WorldMatrix(*(new math::matrix())), 
	  m_LocalMatrix(*(new math::matrix())),
	  m_Enabled(true),
	  m_Visible(true),
	  m_Id(a_Id),
	  m_NodeName(a_NodeName){}

scene_graph_node_base::~scene_graph_node_base(){
	delete &m_WorldMatrix;
	delete &m_LocalMatrix;
}

void scene_graph_node_base::add_child(scene_graph_node_base *a_Node){
	m_Children.push_back(a_Node);
	a_Node->m_Parent = this;
}

void scene_graph_node_base::update_world_matrix(){
	m_WorldMatrix.identity();
	if(m_Parent != 0)
		m_WorldMatrix.concatenate(m_Parent->m_WorldMatrix);
	m_WorldMatrix.concatenate(m_LocalMatrix);
}

void scene_graph::update(scene_graph_node_base *a_Node, float a_DeltaTime){
	if(a_Node == 0){
		return;
	}else if(a_Node->m_Enabled){
		// update the parent
		a_Node->update_world_matrix();
		a_Node->update(a_DeltaTime);

		// now update all the children
		scene_graph_node_base::child_list_t::iterator i = a_Node->m_Children.begin();
		for(; i != a_Node->m_Children.end(); i++){
			update(*i, a_DeltaTime);
		}
	}
}

void scene_graph::visit(scene_graph_node_base *a_Node, scene_graph_visitor *a_Visitor){
	if(a_Node == 0 || a_Visitor == 0){
		return;
	}else if(a_Node->m_Enabled){
		// visit the parent
		a_Node->accept(a_Visitor);

		// visit the children
		scene_graph_node_base::child_list_t::iterator i = a_Node->m_Children.begin();
		for(; i != a_Node->m_Children.end(); i++){
			visit(*i, a_Visitor);
		}
	}
}

scene_graph *scene_graph::create(const std::string &a_Filename){
	parsers::parser_parse_list l;
	scene::scene_graph_loader ll;
    
    std::istream* l_Stream = new std::ifstream(a_Filename.c_str(), std::ios_base::in);
	
	l.parse(*l_Stream);
	l.walk(&ll);
    
    delete l_Stream, l_Stream = 0;

	return ll.get_scene_graph();
}
